﻿package org.papervision3d.core.geom;
import org.papervision3d.core.culling.IObjectCuller;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.math.Matrix3D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.core.proto.GeometryObject3D;
import org.papervision3d.core.render.data.RenderSessionData;
import org.papervision3d.objects.DisplayObject3D;	
/**
* The Vertices3D class lets you create and manipulate groups of vertices.
*
*/
class Vertices3D extends DisplayObject3D
{
	/**
	* Creates a new Vertices3D object.
	*
	*
	* @param	vertices	An array of Vertex3D objects for the vertices of the mesh.
	* <p/>
	*/
	public function new( vertices:Array<Vertex3D>, name:String=null )
	{
		super( name, new GeometryObject3D() ); 
		this.geometry.vertices = vertices != null? vertices : []; 	
	}

	/**
	 * Clones this object.
	 * 
	 * @return	The cloned DisplayObject3D.
	 */ 
	public override function clone():DisplayObject3D
	{
		var object:DisplayObject3D = super.clone();
		var verts:Vertices3D = new Vertices3D(null, object.name);
		
		verts.material = object.material;
		if(object.materials != null)
			verts.materials = object.materials.clone();
			
		if(this.geometry != null)
			verts.geometry = this.geometry.clone(verts);
			
		verts.copyTransform(this);
		
		return verts;
	}
	
	/**
	* Projects three dimensional coordinates onto a two dimensional plane to simulate the relationship of the camera to subject.
	*
	* This is the first step in the process of representing three dimensional shapes two dimensionally.
	*
	* @param	camera		Camera.
	*/
	public override function project( parent :DisplayObject3D,  renderSessionData:RenderSessionData ):Float
	{	
		super.project( parent, renderSessionData );
		
		if( this.culled )
			return 0;
		
		if( Std.is(renderSessionData.camera, IObjectCuller) )
			return projectFrustum(parent, renderSessionData);
		
		if(this.geometry == null || this.geometry.vertices == null)
			return 0;
		
		var res = renderSessionData.camera.projectVertices(this.geometry.vertices, this, renderSessionData);
		//trace("renderSessionData.renderer.renderList" + Reflect.field(renderSessionData.renderer, "renderList"));
		return res;
	}
	
	public function projectEmpty(parent:DisplayObject3D, renderSessionData:RenderSessionData):Float{
		
		return super.project( parent, renderSessionData );
	}

	/**
	 * 
	 * @param	parent
	 * @param	camera
	 * @param	sorted
	 * @return
	 */
	public function projectFrustum( parent :DisplayObject3D, renderSessionData:RenderSessionData ):Float 
	{
		return 0;
	}
	
	/**
	* Calculates 3D bounding box.
	*
	* @return	{min : Number3D, max : Number3D, size : Number3D}
	*/
	public function boundingBox():Dynamic
	{
		var vertices :Array<Vertex3D> = this.geometry.vertices;
		var bBox     :Dynamic = {};

		//bBox.min  = new Number3D(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
		//bBox.max  = new Number3D(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
		bBox.min  = new Number3D(Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY);
		bBox.max  = new Number3D(-Math.POSITIVE_INFINITY, -Math.POSITIVE_INFINITY, -Math.POSITIVE_INFINITY);
		bBox.size = new Number3D();

		for(v in vertices)
		{
			bBox.min.x = Math.min( v.x, bBox.min.x );
			bBox.min.y = Math.min( v.y, bBox.min.y );
			bBox.min.z = Math.min( v.z, bBox.min.z );
			
			bBox.max.x = Math.max( v.x, bBox.max.x );
			bBox.max.y = Math.max( v.y, bBox.max.y );
			bBox.max.z = Math.max( v.z, bBox.max.z );
		}

		bBox.size.x = bBox.max.x - bBox.min.x;
		bBox.size.y = bBox.max.y - bBox.min.y;
		bBox.size.z = bBox.max.z - bBox.min.z;

		return bBox;
	}

	/**
	* Calculates 3D bounding box in world space.
	*
	* @return	{minX, maxX, minY, maxY, minZ, maxZ}
	*/
	
	public function worldBoundingBox () : Dynamic
	{
		
		// TODO requires optimisation : would be better to apply world matrix to local bounds
		var vertices : Array<Vertex3D> = this.geometry.vertices ;
		
		var bBox :Dynamic = {};
		
		bBox.min = new Number3D(Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY);
		bBox.max = new Number3D(-Math.POSITIVE_INFINITY, -Math.POSITIVE_INFINITY, -Math.POSITIVE_INFINITY);
		bBox.size = new Number3D();
		
		var tempV : Number3D ;
		
		for(v in vertices)
		{
			tempV = v.getPosition () ;
			Matrix3D.multiplyVector ( this.world, tempV ) ;
			
			bBox.min.x = Math.min ( tempV.x, bBox.min.x );
			bBox.min.y = Math.min ( tempV.y, bBox.min.y );
			bBox.min.z = Math.min ( tempV.z, bBox.min.z );
			
			bBox.max.x = Math.max ( tempV.x, bBox.max.x );
			bBox.max.y = Math.max ( tempV.y, bBox.max.y );
			bBox.max.z = Math.max ( tempV.z, bBox.max.z );
		}
		
		bBox.size.x = bBox.max.x - bBox.min.x;
		bBox.size.y = bBox.max.y - bBox.min.y;
		bBox.size.z = bBox.max.z - bBox.min.z;
		
		return bBox;
	}

	public function transformVertices( transformation:Matrix3D ):Void
	{
		
		geometry.transformVertices(transformation);

	}
}
